using System;
using System.Data;
using System.Data.SqlServerCe;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace MapMaker
{
    public class MapPackage
    {
        public delegate void TileInsertedHandler(int numberInserted);
        public delegate void PackageCompleteHandler();

        public event TileInsertedHandler TileInserted;
        public event PackageCompleteHandler PackageComplete;

        private SqlCeEngine engine = null;
        private SqlCeConnection conn = null;
        private SqlCeCommand command = null;
        private string connectionString = null;
        private string mapName;
        private string directoryPath;
        private double centerLongitude;
        private double centerLatitude;

        public MapPackage(string MapName, string DirectoryPath, double CenterLongitude, double CenterLatitude)
        {
            mapName = MapName;
            directoryPath = DirectoryPath;
            centerLongitude = CenterLongitude;
            centerLatitude = CenterLatitude;

            connectionString = "Data Source = " + Path.Combine(directoryPath, mapName) + ".map; Max Database Size = 2048;";
        }

        public void CreatePackage()
        {
            engine = new SqlCeEngine(connectionString);

            engine.CreateDatabase();

            command = new SqlCeCommand();
            conn = new SqlCeConnection(connectionString);
            command.Connection = conn;

            conn.Open();

            command.Parameters.Clear();

            command.CommandText = File.OpenText(Path.Combine(Path.Combine(System.Windows.Forms.Application.StartupPath, "template"), "CreateMapTable.sql")).ReadToEnd();
            command.ExecuteNonQuery();

            command.CommandText = File.OpenText(Path.Combine(Path.Combine(System.Windows.Forms.Application.StartupPath, "template"), "CreateMapDetailLevelTable.sql")).ReadToEnd();
            command.ExecuteNonQuery();

            command.CommandText = File.OpenText(Path.Combine(Path.Combine(System.Windows.Forms.Application.StartupPath, "template"), "CreateMapTileTable.sql")).ReadToEnd();
            command.ExecuteNonQuery();

            command.CommandText = File.OpenText(Path.Combine(Path.Combine(System.Windows.Forms.Application.StartupPath, "template"), "CreateMapTileIndex.sql")).ReadToEnd();
            command.ExecuteNonQuery();

            command.CommandText = "INSERT INTO Map VALUES(@MapName, @MapCenterLongitude, @MapCenterLatitude);";
            command.Parameters.Add("@MapName", mapName);
            command.Parameters.Add("@MapCenterLongitude", SqlDbType.Decimal).Value = centerLongitude;
            command.Parameters.Add("@MapCenterLatitude", SqlDbType.Decimal).Value = centerLatitude;
            command.ExecuteNonQuery();

            Program.WorkerThread = new Thread(new ThreadStart(startPackaging));
            Program.WorkerThread.Start();
        }

        private void startPackaging()
        {
            string[] detailLevel = null;
            string[] x = null;
            string[] y = null;
            int tileX = 0;
            int tileY = 0;
            int zoomLevel = 0;
            string imagePath = null;
            FileStream imageStream = null;
            FileInfo image = null;
            int imageLength = 0;
            byte[] imageByteArray = null;
            int numberInserted = 0;
            
            command.CommandText = "INSERT INTO MapTile VALUES(@MapTileX, @MapTileY, @MapTileDetailLevel, @MapTileImage);";
            command.Parameters.Add("@MapTileX", SqlDbType.Int);
            command.Parameters.Add("@MapTileY", SqlDbType.Int);
            command.Parameters.Add("@MapTileDetailLevel", SqlDbType.SmallInt);
            command.Parameters.Add("@MapTileImage", SqlDbType.Image);

            detailLevel = Directory.GetDirectories(directoryPath); 

            foreach (string zoom in detailLevel)
            {
                zoomLevel = int.Parse(Path.GetFileName(zoom));
                y = Directory.GetDirectories(Path.Combine(directoryPath, Path.GetFileName(zoom)));

                foreach (string row in y)
                {
                    x = Directory.GetFiles(Path.Combine(Path.Combine(directoryPath, zoom), Path.GetFileName(row)));
                    tileY = int.Parse(Path.GetFileName(row));

                    foreach (string column in x)
                    {
                        tileX = int.Parse(Path.GetFileNameWithoutExtension(column));
                        imagePath = Path.Combine(Path.Combine(Path.Combine(directoryPath, zoom), Path.GetFileName(row)), Path.GetFileName(column));

                        image = new FileInfo(imagePath);
                        imageLength = Convert.ToInt32(image.Length);
                        imageByteArray = new byte[imageLength];
                        imageStream = new FileStream(imagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                        imageStream.Read(imageByteArray, 0, imageLength);
                        imageStream.Close();
                        

                        command.Parameters["@MapTileX"].Value = tileX;
                        command.Parameters["@MapTileY"].Value = tileY;
                        command.Parameters["@MapTileDetailLevel"].Value = zoomLevel;
                        command.Parameters["@MapTileImage"].Value = imageByteArray;

                        command.ExecuteNonQuery();

                        numberInserted++;

                        if (TileInserted != null)
                        {
                            TileInserted(numberInserted);
                        }

                        Application.DoEvents();
                    }
                }
            }

            command.CommandText = "SELECT DISTINCT MapTileDetailLevel, " +
                                        "MIN(MapTileX) AS MinX, " +
                                        "MAX(MapTileX) AS MaxX, " + 
                                        "MIN(MapTileY) AS MinY, " +
                                        "MAX(MapTileY) AS MaxY " +
                                    "FROM MapTile " +
                                    "GROUP BY MapTileDetailLevel " +
                                    "ORDER BY MapTileDetailLevel;";

            SqlCeDataReader reader = command.ExecuteReader();

            command.CommandText = "INSERT INTO MapDetailLevel VALUES(@DetailLevel, @MinX, @MaxX, @MinY, @MaxY)";
            command.Parameters.Add("@DetailLevel", SqlDbType.SmallInt);
            command.Parameters.Add("@MinX", SqlDbType.Int);
            command.Parameters.Add("@MaxX", SqlDbType.Int);
            command.Parameters.Add("@MinY", SqlDbType.Int);
            command.Parameters.Add("@MaxY", SqlDbType.Int);

            while (reader.Read())
            {
                command.Parameters["@DetailLevel"].Value = reader.GetInt16(0);
                command.Parameters["@MinX"].Value = reader.GetInt32(1);
                command.Parameters["@MaxX"].Value = reader.GetInt32(2);
                command.Parameters["@MinY"].Value = reader.GetInt32(3);
                command.Parameters["@MaxY"].Value = reader.GetInt32(4);

                command.ExecuteNonQuery();
            }

            conn.Close();

            foreach (string dir in Directory.GetDirectories(directoryPath))
            {
                Directory.Delete(dir, true);
            }

            if (PackageComplete != null)
            {
                PackageComplete();
            }

            Application.DoEvents();
        }
    }
}
